﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Exercicio8
{
    class Camera
    {
        #region Protected Variables

        /// <summary>
        /// View Matrix
        /// </summary>
        protected Matrix view;

        /// <summary>
        /// Projection Matrix
        /// </summary>
        protected Matrix projection;

        #endregion

        #region Private Variables
       
        /// <summary>
        /// This var hold a position where the camera will focus
        /// </summary>
        private Vector3 lookAt;

        /// <summary>
        /// This hold the object that the camera will actually follows
        /// </summary>
        private Object3D chasingObject;

        /// <summary>
        /// Esse vector3 segura o resultado da rotação em cima de um Vector3.Zero
        /// </summary>
        private Vector3 rotationDifference;

        #endregion

        #region Public Variables

        /// <summary>
        /// Posição da câmera
        /// </summary>
        public Vector3 position;

        /// <summary>
        /// Valor em graus da rotação X da câmera;
        /// </summary>
        public float rotationX;

        /// <summary>
        /// Valor em graus da rotação Y da câmera;
        /// </summary>
        public float rotationY;

        /// <summary>
        /// Valor em graus da rotação Z da câmera;
        /// </summary>
        public float rotationZ;

        /// <summary>
        /// Esse Vector três segura a diferença de espaço entre a câmera e o objeto sendo seguido;
        /// Usado na câmera enquanto ela está no modo ChaseObject;
        /// </summary>
        public Vector3 positionDifference;

        #endregion


        public Camera(Vector3 position, Vector3 lookAt, float aspectRatio)
        {
            this.position = position;
            this.lookAt = lookAt;
            rotationX = rotationY = rotationZ = 0;

            view = Matrix.CreateLookAt
            (
                position,
                lookAt,
                Vector3.Up
            );

            projection = Matrix.CreatePerspectiveFieldOfView
            (
                MathHelper.PiOver4,
                aspectRatio,
                1.0f,
                1000.0f
            );
        }

        public virtual void Update()
        {
            verifyObjectChasing();
            verifyRotation();
            updateViewMatrix();
        }

        private void updateViewMatrix()
        {
            view = Matrix.CreateLookAt(position - rotationDifference, lookAt, Vector3.Up);
        }

        private void verifyObjectChasing()
        {           
            if(chasingObject != null)
            {
                position = chasingObject.position + positionDifference;
                lookAt = chasingObject.position;
            }
        }

        /// <summary>
        /// This function rotate the camera using quaterniums, but it only works if isn't following a character
        /// </summary>
        private void verifyRotation()
        {
            if (rotationY != 0 || rotationX != 0 || rotationZ != 0)
            {
                Quaternion quat = Quaternion.CreateFromRotationMatrix
                (
                    Matrix.CreateRotationX(MathHelper.ToRadians(rotationX)) *
                    Matrix.CreateRotationY(MathHelper.ToRadians(rotationY)) *
                    Matrix.CreateRotationZ(MathHelper.ToRadians(rotationZ))
                );

                Vector3 difference = position - lookAt;

                Vector3 output;

                Vector3.Transform(ref difference, ref quat, out output);


                rotationDifference = position - output;
            }
        }


        /// <summary>
        /// Essa função serve para que a câmera siga um objeto
        /// </summary>
        /// <param name="objeto">O Objeto a ser seguido</param>
        /// <param name="positionDifference">A difereça de espaço entre o objeto e a câmera</param>
        public virtual void chaseObject(Object3D objeto, Vector3 positionDifference)
        {
            this.chasingObject = objeto;
            this.positionDifference = positionDifference;
        }


        #region GETS & SETS

        public Matrix View
        {
            get
            {
                return view;
            }
            set
            {
                View = value;
            } 
        }

        public Matrix Projection
        {
            get
            {
                return projection;
            }
            set
            {
                projection = value;
            }
        }

        public Vector3 LookAt
        {
            get
            {
                return lookAt;
            }
        }

        #endregion
    }
}
